//  Copyright (C) 2002 Ultr@VNC Team Members. All Rights Reserved.
//  Copyright (C) 2000-2002 Const Kaplinsky. All Rights Reserved.
//  Copyright (C) 2002 RealVNC Ltd. All Rights Reserved.
//  Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
//
//  This file is part of the VNC system.
//
//  The VNC system is free software; you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation; either version 2 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program; if not, write to the Free Software
//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
//  USA.
//
// If the source code for the VNC system is not available from the place 
// whence you received this file, check http://www.uk.research.att.com/vnc or contact
// the authors on vnc@uk.research.att.com for information on obtaining it.

// vncDesktop implementation

// System headers
#include <assert.h>
#include "stdhdrs.h"

// Custom headers
//#include <WinAble.h>
#include <omnithread.h>
#include "winvnc.h"
#include "vnchooks/VNCHooks.h"
#include "vncserver.h"
#include "vnckeymap.h"
#include "rfbRegion.h"
#include "rfbRect.h"
#include "vncdesktop.h"
#include "vncservice.h"
// Modif rdv@2002 - v1.1.x - videodriver
#include "vncOSVersion.h"

#include "mmsystem.h" // sf@2002
#include "TextChat.h" // sf@2002
#include "vncdesktopthread.h"
#include "common/win32_helpers.h"
#include <algorithm>
#include <Commctrl.h>



// Constants
const UINT RFB_SCREEN_UPDATE = RegisterWindowMessage("WinVNC.Update.DrawRect");
const UINT RFB_COPYRECT_UPDATE = RegisterWindowMessage("WinVNC.Update.CopyRect");
const UINT RFB_MOUSE_UPDATE = RegisterWindowMessage("WinVNC.Update.Mouse");
const char szDesktopSink[] = "WinVNC desktop sink";

bool g_Desktop_running;
extern bool g_DesktopThread_running;
extern bool g_update_triggered;
DWORD WINAPI BlackWindow(LPVOID lpParam);

//
// // Modif sf@2002 - v1.1.0 - Optimization
//
// Here we try to speed up the Poll FullScreen function.
// Instead of adding the entire Rect to cached region,
// we divide it in 32/32 pixels blocks and only scan the top-left
// pixel of each block.
// If a pixel has changed, we find the smallest window containing this pixel
// and add the Window rect to the cached region
//
// This function is supposed to be a good compromise between
// speed and efficiency.
// The accuracy has been greatly improved and it now detects very
// small changes on screen that are not detected by the Hooks.
// This new accuracy is obtained by shifting the detection grid 
// in diagonale (the function works with several grids that are tested 
// alternatively). There's probably still room for improvements...
//
// NB: Changes generated by applications using hardware acceleration 
// won't be detected (In order to see a video played on the server using
// Media Player, the vncviewer user must desactivate "Hardware accelation"
// in the Media Player options). 
// 
// 
//
 extern bool G_USE_PIXEL;
PixelCaptureEngine::~PixelCaptureEngine()
{
}

PixelCaptureEngine::PixelCaptureEngine()
	{
		if (OSversion()==2) m_bIsVista=true;
		else 
			m_bIsVista=false;
		if (G_USE_PIXEL)
			m_bIsVista=false;
	}
void
PixelCaptureEngine::PixelCaptureEngineInit(HDC rootdc, HDC memdc, HBITMAP membitmap, bool bCaptureAlpha, void *dibbits, int bpp, int bpr)
	{
		m_hmemdc=memdc;
		m_membitmap=membitmap;
		m_oldbitmap=0;
		m_DIBbits=dibbits;
		m_bCaptureAlpha=bCaptureAlpha;
		m_hrootdc=rootdc;
		m_bytesPerPixel=bpp;
		m_bytesPerRow=bpr;
	}

bool
PixelCaptureEngine::CaptureRect(const rfb::Rect& rect)
	{
		if (m_bIsVista)
		{
				m_rect = rect;
				if ((m_oldbitmap = (HBITMAP) SelectObject(m_hmemdc, m_membitmap)) == NULL)
					return false;

				// Capture screen into bitmap
				BOOL blitok = BitBlt(m_hmemdc, 0, 0, rect.width(), rect.height(), m_hrootdc, rect.tl.x, rect.tl.y, 
									 m_bCaptureAlpha ? (CAPTUREBLT | SRCCOPY) : SRCCOPY);
				return blitok ? true : false;
		}
		return true;
	}

COLORREF
PixelCaptureEngine::CapturePixel(int x, int y)
	{
		if (m_bIsVista)
		{
				COLORREF cr = 0;
				int tx = x - m_rect.tl.x;
				int ty = y - m_rect.tl.y;

				unsigned int index = (m_bytesPerRow * y) + (m_bytesPerPixel * x);
				memcpy(&cr, ((char*)m_DIBbits)+index, m_bytesPerPixel);

				return cr;
		}
		else
		{

				COLORREF cr=0;		
				cr=GetPixel(m_hrootdc, x, y);
				return cr;
		}
	}

void
PixelCaptureEngine::ReleaseCapture()
	{
		// Select the old bitmap back into the memory DC
	if (m_bIsVista) 
		{
				SelectObject(m_hmemdc, m_oldbitmap);
				m_oldbitmap = 0;
		}
	}


bool vncDesktop::FastDetectChanges(rfb::Region2D &rgn, rfb::Rect &rect, int nZone, bool fTurbo)
{
	bool returnvalue=false;
	bool fInitGrid = false;
	bool fIncCycle = false;
	// For more accuracy, we could use 24 or even 16
	const int PIXEL_BLOCK_SIZE  = 32; // Pixels Grid definition
	const int GRID_OFFSET = 4;  // Pixels Grid shifting during cycle (in pixels)
								// The number of grid per zone is PIXEL_BLOCK_SIZE/GRID_OFFSET (must be int !)
	int x, y;
	int xo, yo;
	// WindowsList lWList;
	WindowsList::iterator iWindow;

	// In turbo mode, we clear the list of windows at each iteration -> Lot of updates because 
	//  the same windows can be detected several times during a complete cycle
	// (To avoid this pb, we must update all the grids at the same coordinate when a pixel of 
	// one grid has changed -> later)
	// Otherwise we only clear it each time the Grid cycle loops -> Less updates, less framerate,
	// less CPU, less bandwidth
	if (fTurbo || (m_nGridCycle == 0))
		m_lWList.clear();

	// Create all the Grids (for all the 5 zones (4 quarter screens + 1 full screen)
	// Actually, the quarter screens are not utilized in v1.1.0 but it does not
	// generate any overhead neither additionnal memory consumption (see below)
	if (m_lGridsList.empty())
	{
		for (int i = 0; i < (5 * PIXEL_BLOCK_SIZE / GRID_OFFSET); i++)
		{
			RGBPixelList *pList = new RGBPixelList;
			pList->reserve((rect.height() / PIXEL_BLOCK_SIZE) * (rect.width() / PIXEL_BLOCK_SIZE));
			if (pList != NULL)
			{
				m_lGridsList.push_back(pList);
			    vnclog.Print(LL_INTINFO, VNCLOG("### PixelsGrid %d created !\n"), i);
			}
		}

		hDeskWnd = GetDesktopWindow();
		HWND hWnd = FindWindow(_T("Progman"), _T("Program Manager"));
		if (NULL != hWnd) hWnd = FindWindowEx(hWnd, NULL, "SHELLDLL_DefView", "");
		if (NULL != hWnd) hWnd = FindWindowEx(hWnd, NULL, "SysListView32", "FolderView");
		hFolderView=hWnd;
		if (NULL != hWnd)
			{
			nr_rects = SendMessage(hWnd, LVM_GETITEMCOUNT, 0, 0);
			if (nr_rects>200) nr_rects=200;
			for (int j = 0; j < nr_rects; j++)
                {
					DWORD pid;
					GetWindowThreadProcessId(hWnd, &pid);
					HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, 0, pid);
					RECT* ptritemrect;
					RECT itemrect;
					ptritemrect = (RECT*)VirtualAllocEx(hProcess, NULL, sizeof(RECT), MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
					if (ptritemrect == NULL) {
					   
					} else {
					SendMessage(hWnd, LVM_GETITEMRECT, j, (LPARAM)ptritemrect);
#ifdef _X64
					SIZE_T copied = 0;
#else
					DWORD copied = 0;
#endif
					ReadProcessMemory(hProcess, (void*)ptritemrect, (LPVOID)&itemrect, sizeof(itemrect), &copied);
					rfb::Rect wrect = rfb::Rect(itemrect).intersect(m_Cliprect);
					iconregion.assign_union(wrect);
					VirtualFreeEx(hProcess, ptritemrect, 0, MEM_RELEASE);
					}
				}
			}

	}

	PixelEngine.PixelCaptureEngineInit(m_hrootdc, m_hmemdc, m_membitmap, m_fCaptureAlphaBlending && !m_Black_window_active, 
		                           m_DIBbits, m_scrinfo.format.bitsPerPixel / 8, m_bytesPerRow);
	// We test one zone at a time 
	// vnclog.Print(LL_INTINFO, VNCLOG("### Polling Grid %d - SubGrid %d\n"), nZone, m_nGridCycle); 
	GridsList::iterator iGrid;
	int nGridPos = (nZone * PIXEL_BLOCK_SIZE / GRID_OFFSET) + m_nGridCycle;
	int nIndex = nGridPos;

	iGrid = m_lGridsList.begin();
    std::advance(iGrid, nGridPos);

	RGBPixelList *pThePixelGrid  =  *iGrid;
	RGBPixelList::iterator iPixelColor =  pThePixelGrid->begin();

	if (nZone == 0 || nZone == 4)
	{
	   // vnclog.Print(LL_INTINFO, VNCLOG("### IncCycle Please !\n")); 
	   fIncCycle = true;
	}

	if (pThePixelGrid->empty())
	{
		// vnclog.Print(LL_INTINFO, VNCLOG("### PixelsGrid Init\n"));
		fInitGrid = true;
	}

	int nOffset = GRID_OFFSET * m_nGridCycle;



	PixelEngine.CaptureRect(rect);
	if (PixelEngine.m_bIsVista) returnvalue=true;
	// Try to detect if screen is almost idle
	// no need to poll static screens very fast, it only use cpu
	change_found=0;
	// We walk our way through the Grids
	for (y = rect.tl.y; y < (rect.br.y -nOffset -1) ; y += PIXEL_BLOCK_SIZE)
	{
		yo = y + nOffset;

		for (x = rect.tl.x; x < (rect.br.x -nOffset); x += PIXEL_BLOCK_SIZE)
		{
			xo = x + nOffset;
			bool AlreadyInRegion=rgn.IsPtInRegion(xo,yo);
			// Read the pixel's color on the screen
			COLORREF PixelColor = 0;
			//if (!AlreadyInRegion || fInitGrid ) 
				PixelColor =PixelEngine.CapturePixel(xo, yo);

			// If init list
			if (fInitGrid)
			{
				int off = iPixelColor - pThePixelGrid->begin();
			   pThePixelGrid->push_back(PixelColor);
				iPixelColor = pThePixelGrid->begin() + off;
			   // vnclog.Print(LL_INTINFO, VNCLOG("### PixelsGrid Init : Pixel xo=%d - yo=%d - C=%ld\n"), xo, yo, (long)PixelColor); 
			   continue;
			}

//			vnclog.Print(LL_INTINFO, VNCLOG("### GetPixel %i\n"),OSversion());
			// If the pixel has changed
			if (*iPixelColor != PixelColor)
			{
				change_found=1;
				// Save the new Pixel in the list
				*iPixelColor = PixelColor;
				if (!AlreadyInRegion)
				{
				// Then find the corresponding Window
				POINT point;
				RECT rect;

				point.x = xo;
				point.y = yo;

				// Find the smallest, non-hidden, non-disabled Window containing this pixel
				// REM: We don't use ChildWindowFromPoint because we don't want of hidden windows
				HWND hwnd = WindowFromPoint(point);

				/// This is the fulldesktop, cause a full scan performance !
				if (hFolderView == hwnd && hFolderView)
				{
					if (iconregion.IsPtInRegion(xo,yo))rgn.assign_union(iconregion);
					else
					{
							rect.left=xo-PIXEL_BLOCK_SIZE-m_ScreenOffsetx;
							rect.right=xo+PIXEL_BLOCK_SIZE-m_ScreenOffsetx;
							rect.top=yo-PIXEL_BLOCK_SIZE-m_ScreenOffsetx;
							rect.bottom=yo+PIXEL_BLOCK_SIZE-m_ScreenOffsetx;
							rfb::Rect wrect = rfb::Rect(rect).intersect(m_Cliprect);
							if (!wrect.is_empty())
							{
								rgn.assign_union(wrect);
							}
					}
				}
				// Look if we've already detected this window
				if (hwnd != hDeskWnd  && hFolderView != hwnd)
				{
					
					// Look if we've already detected this window
					if (std::find(m_lWList.begin(), m_lWList.end(), hwnd) == m_lWList.end())
					{			    
						// Add the corresponding rect to the cache region 
						if (GetWindowRect(hwnd, &rect))
							{
								//Buffer coordinates
								rect.left-=m_ScreenOffsetx;
								rect.right-=m_ScreenOffsetx;
								rect.top-=m_ScreenOffsety;
								rect.bottom-=m_ScreenOffsety;
								rfb::Rect wrect = rfb::Rect(rect).intersect(m_Cliprect);
								if (!wrect.is_empty())
									{
/*#ifdef _DEBUG
					char			szText[256];
					DWORD error=GetLastError();
					sprintf(szText,"CheckRect 222222 ++++++++++++++++ %i %i %i %i  \n",wrect.tl.x,wrect.br.x,wrect.tl.y,wrect.br.y);
					SetLastError(0);
					OutputDebugString(szText);		
#endif*/
										rgn.assign_union(wrect);
										m_lWList.insert(hwnd);
									}
							}
					}
				}
			}
			}

			++iPixelColor; // Next PixelColor in the list
		}
	}
	PixelEngine.ReleaseCapture();
	///////////////////////
	// We coun the number of idle detects
	// after x time, force some timeout
	if (change_found)
	{
		idle_counter=0;
	}
	else
	{
		idle_counter=idle_counter+5;
	}
	if (idle_counter>20) 
	{
		//Beep(100,idle_counter);
		Sleep (idle_counter);
	}
	// 250 increased to 500, possible even 1000 will still have a good reaction time
	if (idle_counter>1000) idle_counter=1000;
	///////////////////////

	if (fIncCycle)
	{
	   m_nGridCycle = (m_nGridCycle + 1) % (PIXEL_BLOCK_SIZE / GRID_OFFSET);
	}
	return returnvalue;
}




// Implementation

vncDesktop::vncDesktop()
{
	m_thread = NULL;
#ifdef AVILOG
	AviGen=NULL;
#endif
	m_Black_window_active=false;
	m_hwnd = NULL;
	//m_timerid = 0;
	// adzm - 2010-07 - Fix clipboard hangs
	m_settingClipboardViewer = false;
	m_hnextviewer = NULL;
	m_hcursor = NULL;
	m_hOldcursor = NULL; // sf@2002

	m_displaychanged = FALSE;
	m_update_triggered = FALSE;
	g_update_triggered = FALSE;

	m_hrootdc = NULL;
	m_hmemdc = NULL;
	m_membitmap = NULL;

	// adzm - 2010-07 - Fix clipboard hangs
	//m_initialClipBoardSeen = FALSE;

	m_foreground_window = NULL;

	// Vars for Will Dean's DIBsection patch
	m_DIBbits = NULL;
	m_formatmunged = FALSE;

	m_clipboard_active = FALSE;

	m_pollingcycle = 0;

	// Modif sf@2002 - v1.1.0 
	m_lWList.clear();
    m_lGridsList.clear();
	m_nGridCycle = 0;

	// Modif sf@2002 - v1.1.0
	// m_lLastTempo = 0L;
	m_lLastMouseUpdateTime = 0L;
	m_lLastSlowClientTestTime = timeGetTime();

	// sf@2002 - TextChat - No more used for now
	// m_fTextChatRunning = false;
	// m_pCurrentTextChat = NULL;

	// Modif rdv@2002 - v1.1.x - videodriver
	m_videodriver=NULL;
	m_ScreenOffsetx=0;
	m_ScreenOffsety=0;
	m_hookdriver=false;

	OldPowerOffTimeout=0;

	On_Off_hookdll=false;
	g_Desktop_running=true;
	hUser32=LoadLibrary("USER32");
	if (hUser32) pbi = (pBlockInput)GetProcAddress( hUser32, "BlockInput");
	m_OrigpollingSet=false;
	m_Origpolling=false;
	DriverWantedSet=false;
	can_be_hooked=false;

	multi_monitor=true;
    m_bIsInputDisabledByClient = false;
	m_input_desktop = 0;
	m_home_desktop = 0;
	idle_counter=0;
	trigger_events[0]=CreateEvent(NULL,TRUE,FALSE,"timer");
	trigger_events[1]=CreateEvent(NULL,TRUE,FALSE,"screenupdate");
	trigger_events[2]=CreateEvent(NULL,TRUE,FALSE,"mouseupdate");
	trigger_events[3]=CreateEvent(NULL,TRUE,FALSE,"user1");
	trigger_events[4]=CreateEvent(NULL,TRUE,FALSE,"user2");
	trigger_events[5]=CreateEvent(NULL,TRUE,FALSE,"quit");
	restart_event=CreateEvent(NULL,TRUE,TRUE,"restart");
	rgnpump.clear();
	lock_region_add=false;
	InitWindowThreadh=NULL;
	old_Blockinput=2;
	old_Blockinput1=2;
	old_Blockinput2=2;
	nr_rects=0;
	iconregion.clear();
	blankmonitorstate=false;

// JnZn558
	m_current_monitor = MULTI_MON_PRIMARY;
	m_old_monitor = MULTI_MON_PRIMARY;
	m_ScreenWidth = 0;
	m_ScreenHeight = 0;
//
}

vncDesktop::~vncDesktop()
{
	vnclog.Print(LL_INTINFO, VNCLOG("~vncDesktop \n"));

	// If we created a thread then here we delete it
	// The thread itself does most of the cleanup
	if(m_thread != NULL)
	{
		StopInitWindowthread();
		vncDesktopThread *thread=(vncDesktopThread*)m_thread;
		int counter=0;
		while (g_DesktopThread_running!=false)
		{
			if (Window()==NULL)SetEvent(trigger_events[5]);;
			Sleep(100);
			counter++;
			if (counter>50) 
			{
				vnclog.Print(LL_INTINFO, VNCLOG("Desktop thread running, force close \n"));
				SetEvent(trigger_events[5]);
				break;
			}
		}
		// Join with the desktop handler thread
		void *returnval;
		m_thread->join(&returnval);
		m_thread = NULL;
	}



	// added jeff
    SetBlockInputState(false);
	// Let's call Shutdown just in case something went wrong...
	Shutdown();
	vnclog.Print(LL_INTINFO, VNCLOG("~vncDesktop Shutdown()\n"));
	// Modif sf@2002
	m_lWList.clear();
	GridsList::iterator iGrid;
	for (iGrid = m_lGridsList.begin(); iGrid != m_lGridsList.end(); iGrid++)
	{
		if (*iGrid)
		{
			// Since we've replaced this:
			// "typedef std::list<RGBPixelList*> GridsList;"
			// with this:
			// "typedef std::list<void*> GridsList;"
			// we must be carefull to avoid memory leaks...
			((RGBPixelList*)(*iGrid))->clear();
			delete ((RGBPixelList*)(*iGrid));
			vnclog.Print(LL_INTWARN, VNCLOG("delete ((RGBPixelList) \n"));
		}
	}
	vnclog.Print(LL_INTINFO, VNCLOG("~vncDesktop m_lGridsList.clear\n"));
	m_lGridsList.clear();
	if (hUser32) FreeLibrary(hUser32);
	g_Desktop_running=false;
	for (int i=0;i<6;i++)
	CloseHandle(trigger_events[i]);
	CloseHandle(restart_event);
	//problems, sync could be restarted in the little time the desktop thread was still running
	//then this doesn't exist on desktop close and sink window crash
	// Fix E. SAG
	if (InitWindowThreadh)
	{
      vnclog.Print(LL_INTERR, VNCLOG("~vncDesktop:: second request to close InitWindowthread\n"));
      StopInitWindowthread();
	}
}


// Tell the desktop hooks to grab & update a particular rectangle
void
vncDesktop::QueueRect(const rfb::Rect &rect)
{
	//Full screen Update
	SetEvent(trigger_events[4]);
}
	
// Kick the desktop hooks to perform an update
void
vncDesktop::TriggerUpdate()
{
	// Note that we should really lock the update lock here,
	// but there are periodic timer updates anyway, so
	// we don't actually need to.  Something to think about.
	if (!m_update_triggered) {
		m_update_triggered = TRUE;
		g_update_triggered = TRUE;
		SetEvent(trigger_events[0]);
	}
}


// Routine to startup and install all the hooks and stuff
DWORD
vncDesktop::Startup()
{
	// Initialise the Desktop object
    DWORD status;
	if (!InitDesktop())
		{
			vnclog.Print(LL_INTINFO, VNCLOG("InitDesktop Failed\n"));
			return ERROR_DESKTOP_INIT_FAILED;
		}

	// Modif rdv@2002 - v1.1.x - videodriver
	vnclog.Print(LL_INTINFO, VNCLOG("InitVideo driver Called\n"));
	if (FALSE != DriverWantedSet)
			{
			m_server->Driver(DriverWanted);
			m_server->Hook(HookWanted);
			DriverWantedSet=FALSE;
			}
	if (m_server->Driver())
				{
					vnclog.Print(LL_INTINFO, VNCLOG("Driver option enabled \n"));
					if(OSversion()==1 || OSversion()==2 )
						{
							//Enable only the video driver for the Default desktop
							HDESK desktop = GetThreadDesktop(GetCurrentThreadId());
							DWORD dummy;
							char new_name[256];
							if (GetUserObjectInformation(desktop, UOI_NAME, &new_name, 256, &dummy))
								{
									if (strcmp(new_name,"Default")==0)
										{
											InitVideoDriver();
										}
									else 
										vnclog.Print(LL_INTINFO, VNCLOG("no default desktop \n"));
								}
						}
				}
    else
	vnclog.Print(LL_INTINFO, VNCLOG("Driver option disabled \n"));
	if (m_Origpolling) m_server->PollFullScreen(m_Origpolling);
	m_OrigpollingSet=false;
	
	if (VideoBuffer())
	{
		vnclog.Print(LL_INTINFO, VNCLOG("Break log\n"));
	}
	if ((status = InitBitmap()) != 0)
		{
			vnclog.Print(LL_INTINFO, VNCLOG("InitBitmap Failed\n"));
			return status;
		}

	if (!ThunkBitmapInfo())
		{
			vnclog.Print(LL_INTINFO, VNCLOG("ThunkBitmapInfo Failed\n"));
			return FALSE;
		}

	if (VideoBuffer())
	{
		vnclog.Print(LL_INTINFO, VNCLOG("Removing real Dib buffer and replace by driver communication buffer\n"));
		if (m_membitmap != NULL)
			{
				DeleteObject(m_membitmap);
				m_membitmap = NULL;
			}
		m_DIBbits=m_videodriver->myframebuffer;
		pchanges_buf=m_videodriver->mypchangebuf;
		m_buffer.VideDriverUsed(true);
		InvalidateRect(NULL,NULL,TRUE);
	}
    else if ((status = EnableOptimisedBlits()) != 0)
    {
		vnclog.Print(LL_INTINFO, VNCLOG("EnableOptimisedBlits Failed\n"));
		m_buffer.VideDriverUsed(false);
		return status;
    }
	else m_buffer.VideDriverUsed(false);

	if ((status = SetPixFormat()) != 0)
		{
		vnclog.Print(LL_INTINFO, VNCLOG("SetPixFormat Failed\n"));
		return status;
		}

	if (!SetPixShifts())
		{
		vnclog.Print(LL_INTINFO, VNCLOG("SetPixShift Failed\n"));
		return ERROR_DESKTOP_UNSUPPORTED_PIXEL_FORMAT;
		}
	
	if (!SetPalette())
		{
		vnclog.Print(LL_INTINFO, VNCLOG("SetPalette Failed\n"));
		return ERROR_DESKTOP_NO_PALETTE;
		}
	
		StartInitWindowthread();

	// Start a timer to handle Polling Mode.  The timer will cause
	// an "idle" event once every 1/10 second, which is necessary if Polling
	// Mode is being used, to cause TriggerUpdate to be called.


	// Initialise the buffer object
	if (!m_buffer.SetDesktop(this))
        return ERROR_DESKTOP_OUT_OF_MEMORY;
	GetQuarterSize();
#ifdef AVILOG
	if(vnclog.GetVideo()){
		SYSTEMTIME lt;    
		GetLocalTime(&lt);
		char str[MAX_PATH + 32]; // 29 January 2008 jdp 
		_snprintf(str, sizeof str, "%02d_%02d_%02d_%02d_%02d", lt.wMonth,lt.wDay,lt.wHour, lt.wMinute,lt.wSecond);
		strcat(str,"_vnc.avi");
		AviGen = new CAVIGenerator(str,"c:\\temp",&m_bminfo.bmi.bmiHeader,5);
		HRESULT hr;
		hr=AviGen->InitEngine();
		if (FAILED(hr))
		{
			AviGen->ReleaseEngine(); 
			delete AviGen;
			AviGen=NULL;
		}

	}
#endif
	// Everything is ok, so return success
	return 0;
}

// Routine to shutdown all the hooks and stuff
BOOL
vncDesktop::Shutdown()
{
#ifdef AVILOG
	if (AviGen)
		{
			AviGen->ReleaseEngine(); 
			delete AviGen;
			AviGen=NULL;
		}
#endif

	ShutdownInitWindowthread();

	// Now free all the bitmap stuff
	if (m_hrootdc != NULL)
	{
		if (!DeleteDC(m_hrootdc))
				vnclog.Print(LL_INTERR, VNCLOG("failed to DeleteDC hrootdc\n"));
		m_hrootdc = NULL;
	}
	if (m_hmemdc != NULL)
	{
		// Release our device context
		if (!DeleteDC(m_hmemdc))
		{
			vnclog.Print(LL_INTERR, VNCLOG("failed to DeleteDC hmemdc\n"));
		}
		m_hmemdc = NULL;
	}
	if (m_membitmap != NULL)
	{
		// Release the custom bitmap, if any
		if (!DeleteObject(m_membitmap))
		{
			vnclog.Print(LL_INTERR, VNCLOG("failed to DeleteObject\n"));
		}
		m_membitmap = NULL;
	}

    m_DIBbits = NULL;

    if (m_hcursor)
    {
        DeleteObject(m_hcursor);
        m_hcursor = NULL;
    }
    if (m_hOldcursor)
    {
        DeleteObject(m_hOldcursor);
        m_hOldcursor = NULL;
    }
	// Modif rdv@2002 - v1.1.x - videodriver
	ShutdownVideoDriver();

	if (m_home_desktop)
		vncService::SelectHDESK(m_home_desktop);
	if (m_input_desktop)
	{
		if (!CloseDesktop(m_input_desktop))
			vnclog.Print(LL_INTERR, VNCLOG("failed to close desktop\n"));
		m_input_desktop = 0;
	}
	GridsList::iterator iGrid;
	for (iGrid = m_lGridsList.begin(); iGrid != m_lGridsList.end(); iGrid++)
	{
		if (*iGrid)
		{
			// Since we've replaced this:
			// "typedef std::list<RGBPixelList*> GridsList;"
			// with this:
			// "typedef std::list<void*> GridsList;"
			// we must be carefull to avoid memory leaks...
			((RGBPixelList*)(*iGrid))->clear();
			delete ((RGBPixelList*)(*iGrid));
			vnclog.Print(LL_INTWARN, VNCLOG("delete ((RGBPixelList) \n"));
		}
	}
	m_lGridsList.clear();
	m_foreground_window_rect.clear();
	return TRUE;
}

// Routine to ensure we're on the correct NT desktop

BOOL
vncDesktop::InitDesktop()
{
	int result=vncService::InputDesktopSelected();
	if (result==1 || result==2)
		return TRUE;
	vnclog.Print(LL_INTINFO, VNCLOG("InitDesktop...\n"));
	return vncService::SelectDesktop(NULL, &m_input_desktop);
}

// Routine used to close the screen saver, if it's active...

BOOL CALLBACK
KillScreenSaverFunc(HWND hwnd, LPARAM lParam)
{
	char buffer[256];

	// - ONLY try to close Screen-saver windows!!!
	if ((GetClassName(hwnd, buffer, 256) != 0) &&
		(strcmp(buffer, "WindowsScreenSaverClass") == 0))
		PostMessage(hwnd, WM_CLOSE, 0, 0);
	return TRUE;
}

void
vncDesktop::KillScreenSaver()
{
	OSVERSIONINFO osversioninfo;
	osversioninfo.dwOSVersionInfoSize = sizeof(osversioninfo);

	// Get the current OS version
	if (!GetVersionEx(&osversioninfo))
		return;

	vnclog.Print(LL_INTINFO, VNCLOG("KillScreenSaver...\n"));

	// How to kill the screen saver depends on the OS
	switch (osversioninfo.dwPlatformId)
	{
	case VER_PLATFORM_WIN32_WINDOWS:
		{
			// Windows 95

			// Fidn the ScreenSaverClass window
			HWND hsswnd = FindWindow ("WindowsScreenSaverClass", NULL);
			if (hsswnd != NULL)
				PostMessage(hsswnd, WM_CLOSE, 0, 0); 
			break;
		} 
	case VER_PLATFORM_WIN32_NT:
		{
			// Windows NT

			// Find the screensaver desktop
			HDESK hDesk = OpenDesktop(
				"Screen-saver",
				0,
				FALSE,
				DESKTOP_READOBJECTS | DESKTOP_WRITEOBJECTS
				);
			if (hDesk != NULL)
			{
				vnclog.Print(LL_INTINFO, VNCLOG("Killing ScreenSaver\n"));

				// Close all windows on the screen saver desktop
				EnumDesktopWindows(hDesk, (WNDENUMPROC) &KillScreenSaverFunc, 0);
				CloseDesktop(hDesk);
				// Pause long enough for the screen-saver to close
				//Sleep(2000);
				// Reset the screen saver so it can run again
				SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, TRUE, 0, SPIF_SENDWININICHANGE); 
			}
			break;
		}
	}
}


//
// Modif sf@2002 - Single Window
//
BOOL CALLBACK EnumWindowsHnd(HWND hwnd, LPARAM arg)
{
 int  nRet;
 char buffer[128];
 char szNameAppli[16];

 strncpy(szNameAppli, (LPSTR)(((vncDesktop*)arg)->GetServerPointer()->GetWindowName()),15);
 szNameAppli[15]=0;
 nRet = GetWindowText(hwnd, buffer, sizeof(buffer));
 if (nRet > 0)
 {
  if ( !_strnicmp(buffer, szNameAppli, lstrlen(szNameAppli)))
  {
       ((vncDesktop*)arg)->m_Single_hWnd = hwnd;
       return FALSE;
  }
  else
	  return TRUE;
 }
 return TRUE;
}


DWORD
vncDesktop::InitBitmap()
{	
	// Get the device context for the whole screen and find it's size
	DriverType=NONE;
	if (OSversion()==1 || OSversion()==2) //XP W2k
		{	
			if (VideoBuffer())
				{
					pEnumDisplayDevices pd=NULL;
					LPSTR driverName = "mv video hook driver2";
					BOOL DriverFound;
					DEVMODE devmode;
					FillMemory(&devmode, sizeof(DEVMODE), 0);
					devmode.dmSize = sizeof(DEVMODE);
					devmode.dmDriverExtra = 0;
					BOOL change = EnumDisplaySettings(NULL,ENUM_CURRENT_SETTINGS,&devmode);
					devmode.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;
					HMODULE hUser32=LoadLibrary("USER32");
					if (hUser32) pd = (pEnumDisplayDevices)GetProcAddress( hUser32, "EnumDisplayDevicesA");
						if (pd)
							{
								LPSTR deviceName=NULL;
								DISPLAY_DEVICE dd;
								ZeroMemory(&dd, sizeof(dd));
								dd.cb = sizeof(dd);
								devmode.dmDeviceName[0] = '\0';
								INT devNum = 0;
								BOOL result;
								DriverFound=false;
								while (result = (*pd)(NULL,devNum, &dd,0))
									{
										if (strcmp((const char *)&dd.DeviceString[0], driverName) == 0)
											{
												DriverFound=true;
												break;
											}
										devNum++;
									}
								if (DriverFound)
									{
										deviceName = (LPSTR)&dd.DeviceName[0];
										// JnZn558 m_hrootdc = CreateDC("DISPLAY",deviceName,NULL,NULL);
										m_hrootdc = GetDC(NULL);
										BOOL change = EnumDisplaySettings(deviceName,ENUM_CURRENT_SETTINGS,&devmode);
										m_ScreenOffsetx=devmode.dmPosition.x;
										m_ScreenOffsety=devmode.dmPosition.y;
										if (m_hrootdc) DriverType=MIRROR;
										Checkmonitors();
										requested_multi_monitor=m_buffer.IsMultiMonitor();
										multi_monitor=false;
										if (requested_multi_monitor && nr_monitors>1) multi_monitor=true;
									}
							}
					if (hUser32) FreeLibrary(hUser32);
				}//VIDEOBUFFER
			}//OS

	// RDV SINGLE WINDOW
	if (m_server->SingleWindow() && m_Single_hWnd==NULL)
	{		
		EnumWindows((WNDENUMPROC)EnumWindowsHnd, (LPARAM) this);
	}


	if (m_hrootdc == NULL) {
		vnclog.Print(LL_INTERR, VNCLOG("No driver used \n"));
		//Multi-Monitor changes
		Checkmonitors();
		requested_multi_monitor=m_buffer.IsMultiMonitor();
		multi_monitor=false;
		if (requested_multi_monitor && nr_monitors>1) multi_monitor=true;
		
		/* JnZn558
		if (!multi_monitor)
		{
			m_hrootdc = CreateDC(("DISPLAY"),mymonitor[0].device,NULL,NULL);
			m_ScreenOffsetx=mymonitor[0].offsetx;
			m_ScreenOffsety=mymonitor[0].offsety;
		}
		else
		{
			m_hrootdc = GetDC(NULL);
			
			m_ScreenOffsetx=mymonitor[2].offsetx;
			m_ScreenOffsety=mymonitor[2].offsety;
			
			
		}
		*/
		m_hrootdc = GetDC(NULL);
		if (m_hrootdc == NULL) {
				vnclog.Print(LL_INTERR, VNCLOG("Failed m_rootdc \n"));
				return ERROR_DESKTOP_NO_ROOTDC;
		}
		
	}

	m_ScreenOffsetx=mymonitor[3].offsetx;
	m_ScreenOffsety=mymonitor[3].offsety;
	m_ScreenWidth = mymonitor[3].Width;
	m_ScreenHeight = mymonitor[3].Height;
	
	// JnZn558 if (multi_monitor && !VideoBuffer()) m_bmrect = rfb::Rect(0, 0,mymonitor[2].Width,mymonitor[2].Height);
	if (multi_monitor && !VideoBuffer()) m_bmrect = rfb::Rect(0, 0,mymonitor[3].Width,mymonitor[3].Height);
	else if (!VideoBuffer()) m_bmrect = rfb::Rect(0, 0,mymonitor[0].Width,mymonitor[0].Height);
	// JnZn558 else m_bmrect = rfb::Rect(0, 0,GetDeviceCaps(m_hrootdc, HORZRES),GetDeviceCaps(m_hrootdc, VERTRES));
	else m_bmrect = rfb::Rect(m_ScreenOffsetx, m_ScreenOffsety,m_ScreenWidth,m_ScreenHeight);
	vnclog.Print(LL_INTINFO, VNCLOG("bitmap dimensions are %d x %d\n"), m_bmrect.br.x, m_bmrect.br.y);

	// Create a compatible memory DC
	m_hmemdc = CreateCompatibleDC(m_hrootdc);
	if (m_hmemdc == NULL) {
		vnclog.Print(LL_INTERR, VNCLOG("failed to create compatibleDC(%d)\n"), GetLastError());
		return ERROR_DESKTOP_NO_ROOTDC;
	}

	// Check that the device capabilities are ok
	if ((GetDeviceCaps(m_hrootdc, RASTERCAPS) & RC_BITBLT) == 0)
	{
		MessageBoxSecure(
			NULL,
			"vncDesktop : root device doesn't support BitBlt\n"
			"WinVNC cannot be used with this graphic device driver",
			szAppName,
			MB_ICONSTOP | MB_OK
			);
		return ERROR_DESKTOP_NO_BITBLT;
	}
	if ((GetDeviceCaps(m_hmemdc, RASTERCAPS) & RC_DI_BITMAP) == 0)
	{
		MessageBoxSecure(
			NULL,
			"vncDesktop : memory device doesn't support GetDIBits\n"
			"WinVNC cannot be used with this graphics device driver",
			szAppName,
			MB_ICONSTOP | MB_OK
			);
		return ERROR_DESKTOP_NO_GETDIBITS;
	}

	// Create the bitmap to be compatible with the ROOT DC!!!
	m_membitmap = CreateCompatibleBitmap(m_hrootdc,1,1); //m_bmrect.br.x, m_bmrect.br.y);
	if (m_membitmap == NULL) {
		vnclog.Print(LL_INTERR, VNCLOG("failed to create memory bitmap(%d)\n"), GetLastError());
		return ERROR_DESKTOP_NO_COMPATBITMAP;
	}
	vnclog.Print(LL_INTINFO, VNCLOG("created memory bitmap\n"));

	// Get the bitmap's format and colour details
	int result;
	memset(&m_bminfo, 0, sizeof(m_bminfo));
	m_bminfo.bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
	m_bminfo.bmi.bmiHeader.biBitCount = 0;
	result = ::GetDIBits(m_hmemdc, m_membitmap, 0, 1, NULL, &m_bminfo.bmi, DIB_RGB_COLORS);
	if (result == 0) {
		vnclog.Print(LL_INTERR, VNCLOG("unable to get display format\n"));
		return ERROR_DESKTOP_NO_DISPLAYFORMAT;
	}
	result = ::GetDIBits(m_hmemdc, m_membitmap,  0, 1, NULL, &m_bminfo.bmi, DIB_RGB_COLORS);
	if (result == 0) {
		vnclog.Print(LL_INTERR, VNCLOG("unable to get display colour info\n"));
		return ERROR_DESKTOP_NO_DISPLAYFORMAT;
	}
	vnclog.Print(LL_INTINFO, VNCLOG("got bitmap format\n"));

	// Henceforth we want to use a top-down scanning representation
    m_bminfo.bmi.bmiHeader.biWidth = m_bmrect.br.x;
    m_bminfo.bmi.bmiHeader.biHeight = m_bmrect.br.y;
    m_bminfo.bmi.bmiHeader.biSizeImage = abs((m_bminfo.bmi.bmiHeader.biWidth *
				m_bminfo.bmi.bmiHeader.biHeight *
				m_bminfo.bmi.bmiHeader.biBitCount)/ 8);
	m_bminfo.bmi.bmiHeader.biHeight = - abs(m_bminfo.bmi.bmiHeader.biHeight);

	// Is the bitmap palette-based or truecolour?
	m_bminfo.truecolour = (GetDeviceCaps(m_hmemdc, RASTERCAPS) & RC_PALETTE) == 0;
	InvalidateRect(NULL,NULL,TRUE);
	return 0;
}

BOOL
vncDesktop::ThunkBitmapInfo()
{
	// If we leave the pixel format intact, the blist can be optimised (Will Dean's patch)
	m_formatmunged = FALSE;

	// HACK ***.  Optimised blits don't work with palette-based displays, yet
	if (!m_bminfo.truecolour) {
		m_formatmunged = TRUE;
	}

	// Attempt to force the actual format into one we can handle
	// We can handle 8-bit-palette and 16/32-bit-truecolour modes
	switch (m_bminfo.bmi.bmiHeader.biBitCount)
	{
	case 1:
	case 4:
		vnclog.Print(LL_INTINFO, VNCLOG("DBG:used/bits/planes/comp/size = %d/%d/%d/%d/%d\n"),
			(int)m_bminfo.bmi.bmiHeader.biClrUsed,
			(int)m_bminfo.bmi.bmiHeader.biBitCount,
			(int)m_bminfo.bmi.bmiHeader.biPlanes,
			(int)m_bminfo.bmi.bmiHeader.biCompression,
			(int)m_bminfo.bmi.bmiHeader.biSizeImage);
		
		// Correct the BITMAPINFO header to the format we actually want
		m_bminfo.bmi.bmiHeader.biClrUsed = 0;
		m_bminfo.bmi.bmiHeader.biPlanes = 1;
		m_bminfo.bmi.bmiHeader.biCompression = BI_RGB;
		m_bminfo.bmi.bmiHeader.biBitCount = 8;
		m_bminfo.bmi.bmiHeader.biSizeImage =
			abs((m_bminfo.bmi.bmiHeader.biWidth *
				m_bminfo.bmi.bmiHeader.biHeight *
				m_bminfo.bmi.bmiHeader.biBitCount)/ 8);
		m_bminfo.bmi.bmiHeader.biClrImportant = 0;
		m_bminfo.truecolour = FALSE;

		// Display format is non-VNC compatible - use the slow blit method
		m_formatmunged = TRUE;
		break;	
	case 24:
		// Update the bitmapinfo header
		m_bminfo.bmi.bmiHeader.biBitCount = 32;
		m_bminfo.bmi.bmiHeader.biPlanes = 1;
		m_bminfo.bmi.bmiHeader.biCompression = BI_RGB;
		m_bminfo.bmi.bmiHeader.biSizeImage =
			abs((m_bminfo.bmi.bmiHeader.biWidth *
				m_bminfo.bmi.bmiHeader.biHeight *
				m_bminfo.bmi.bmiHeader.biBitCount)/ 8);
		// Display format is non-VNC compatible - use the slow blit method
		m_formatmunged = TRUE;
		break;
	}

	return TRUE;
}

DWORD
vncDesktop::SetPixFormat()
{
 // If we are using a memory bitmap then check how many planes it uses
  // The VNC code can only handle formats with a single plane (CHUNKY pixels)
  if (!m_DIBbits) {
	  vnclog.Print(LL_INTINFO, VNCLOG("DBG:display context has %d planes!\n"),
      GetDeviceCaps(m_hrootdc, PLANES));
	  vnclog.Print(LL_INTINFO, VNCLOG("DBG:memory context has %d planes!\n"),
      GetDeviceCaps(m_hmemdc, PLANES));
	  if (GetDeviceCaps(m_hmemdc, PLANES) != 1)
	  {
		  MessageBoxSecure(
			  NULL,
			  "vncDesktop : current display is PLANAR, not CHUNKY!\n"
			  "WinVNC cannot be used with this graphics device driver",
			  szAppName,
			  MB_ICONSTOP | MB_OK
			  );
          return ERROR_DESKTOP_UNSUPPORTED_PIXEL_ORGANIZATION;
	  }
  }
	
	
	// Examine the bitmapinfo structure to obtain the current pixel format
	m_scrinfo.format.trueColour = m_bminfo.truecolour;
	m_scrinfo.format.bigEndian = 0;

	// Set up the native buffer width, height and format
	/* JnZn558
	m_scrinfo.framebufferWidth = (CARD16) (m_bmrect.br.x - m_bmrect.tl.x);		// Swap endian before actually sending
	m_scrinfo.framebufferHeight = (CARD16) (m_bmrect.br.y - m_bmrect.tl.y);	// Swap endian before actually sending
	*/

	m_scrinfo.framebufferWidth = (CARD16) (m_bmrect.br.x - ((m_bmrect.tl.x < 0)? 0: m_bmrect.tl.x));		// Swap endian before actually sending
	m_scrinfo.framebufferHeight = (CARD16) (m_bmrect.br.y - ((m_bmrect.tl.y < 0)? 0: m_bmrect.tl.y));	// Swap endian before actually sending
	m_scrinfo.format.bitsPerPixel = (CARD8) m_bminfo.bmi.bmiHeader.biBitCount;
	m_scrinfo.format.depth        = (CARD8) m_bminfo.bmi.bmiHeader.biBitCount;

	// Calculate the number of bytes per row
	m_bytesPerRow = m_scrinfo.framebufferWidth * m_scrinfo.format.bitsPerPixel / 8;

	return 0;
}

BOOL
vncDesktop::SetPixShifts()
{
	// Sort out the colour shifts, etc.
	DWORD redMask=0, blueMask=0, greenMask = 0;

	switch (m_bminfo.bmi.bmiHeader.biBitCount)
	{
	case 16:
		// Standard 16-bit display
		if (m_bminfo.bmi.bmiHeader.biCompression == BI_RGB)
		{
			// each word single pixel 5-5-5
			redMask = 0x7c00; greenMask = 0x03e0; blueMask = 0x001f;
		}
		else
		{
			if (m_bminfo.bmi.bmiHeader.biCompression == BI_BITFIELDS)
			{
				redMask =   *(DWORD *) &m_bminfo.bmi.bmiColors[0];
				greenMask = *(DWORD *) &m_bminfo.bmi.bmiColors[1];
				blueMask =  *(DWORD *) &m_bminfo.bmi.bmiColors[2];
			}
		}
		break;

	case 32:
		// Standard 24/32 bit displays
		if (m_bminfo.bmi.bmiHeader.biCompression == BI_RGB)
		{
			redMask = 0xff0000; greenMask = 0xff00; blueMask = 0x00ff;
		}
		else
		{
			if (m_bminfo.bmi.bmiHeader.biCompression == BI_BITFIELDS)
			{
				redMask =   *(DWORD *) &m_bminfo.bmi.bmiColors[0];
				greenMask = *(DWORD *) &m_bminfo.bmi.bmiColors[1];
				blueMask =  *(DWORD *) &m_bminfo.bmi.bmiColors[2];
			}
		}
		break;

	default:
		// Other pixel formats are only valid if they're palette-based
		if (m_bminfo.truecolour)
		{
			vnclog.Print(LL_INTERR, "unsupported truecolour pixel format for setpixshifts\n");
			return FALSE;
		}
		return TRUE;
	}

	// Convert the data we just retrieved
	MaskToMaxAndShift(redMask, m_scrinfo.format.redMax, m_scrinfo.format.redShift);
	MaskToMaxAndShift(greenMask, m_scrinfo.format.greenMax, m_scrinfo.format.greenShift);
	MaskToMaxAndShift(blueMask, m_scrinfo.format.blueMax, m_scrinfo.format.blueShift);

	return TRUE;
}

BOOL
vncDesktop::SetPalette()
{
	// Lock the current display palette into the memory DC we're holding
	// *** CHECK THIS FOR LEAKS!
    if (VideoBuffer())
        return TRUE;
	if (!m_bminfo.truecolour)
	{
		if (!m_DIBbits)
		{
			// - Handle the palette for a non DIB-Section
			
			LOGPALETTE *palette;
			UINT size = sizeof(LOGPALETTE) + (sizeof(PALETTEENTRY) * 256);
			
			palette = (LOGPALETTE *) new char[size];
			if (palette == NULL) {
				vnclog.Print(LL_INTERR, VNCLOG("unable to allocate logical palette\n"));
				return FALSE;
			}
			
			// Initialise the structure
			palette->palVersion = 0x300;
			palette->palNumEntries = 256;
			
			// Get the system colours
			if (GetSystemPaletteEntries(m_hrootdc,
				0, 256, palette->palPalEntry) == 0)
			{
				vnclog.Print(LL_INTERR, VNCLOG("unable to get system palette entries\n"));
				delete [] palette;
				return FALSE;
			}
			
			// Create a palette from those
			HPALETTE pal = CreatePalette(palette);
			if (pal == NULL)
			{
				vnclog.Print(LL_INTERR, VNCLOG("unable to create HPALETTE\n"));
				delete [] palette;
				return FALSE;
			}
			
			// Select the palette into our memory DC
			HPALETTE oldpalette = SelectPalette(m_hmemdc, pal, FALSE);
			if (oldpalette == NULL)
			{
				vnclog.Print(LL_INTERR, VNCLOG("unable to select() HPALETTE\n"));
				delete [] palette;
				DeleteObject(pal);
				return FALSE;
			}
			
			// Worked, so realise the palette
			if (RealizePalette(m_hmemdc) == GDI_ERROR)
				vnclog.Print(LL_INTWARN, VNCLOG("warning - failed to RealizePalette\n"));
			
			// It worked!
			delete [] palette;
			DeleteObject(oldpalette);
			
			vnclog.Print(LL_INTINFO, VNCLOG("initialised palette OK\n"));
			return TRUE;
		}
		else
		{
			// - Handle a DIB-Section's palette
			
			// - Fetch the system palette for the framebuffer
			PALETTEENTRY syspalette[256];
			UINT entries = ::GetSystemPaletteEntries(m_hrootdc, 0, 256, syspalette);
			vnclog.Print(LL_INTERR, VNCLOG("framebuffer has %u palette entries"), entries);
			
			// - Store it and convert it to RGBQUAD format
			RGBQUAD dibpalette[256];
			unsigned int i;
			for (i=0;i<entries;i++) {
				dibpalette[i].rgbRed = syspalette[i].peRed;
				dibpalette[i].rgbGreen = syspalette[i].peGreen;
				dibpalette[i].rgbBlue = syspalette[i].peBlue;
				dibpalette[i].rgbReserved = 0;
			}
			
			// - Set the rest of the palette to something nasty but usable
			for (i=entries;i<256;i++) {
				dibpalette[i].rgbRed = i % 2 ? 255 : 0;
				dibpalette[i].rgbGreen = i/2 % 2 ? 255 : 0;
				dibpalette[i].rgbBlue = i/4 % 2 ? 255 : 0;
				dibpalette[i].rgbReserved = 0;
			}
			
			// - Update the DIB section to use the same palette
			HDC bitmapDC=::CreateCompatibleDC(m_hrootdc);
			if (!bitmapDC) {
				vnclog.Print(LL_INTERR, VNCLOG("unable to create temporary DC"), GetLastError());
				return FALSE;
			}
			HBITMAP old_bitmap = (HBITMAP)::SelectObject(bitmapDC, m_membitmap);
			if (!old_bitmap) {
				vnclog.Print(LL_INTERR, VNCLOG("unable to select DIB section into temporary DC"), GetLastError());
                DeleteDC(bitmapDC);
				return FALSE;
			}
			UINT entries_set = ::SetDIBColorTable(bitmapDC, 0, 256, dibpalette);
			if (entries_set == 0) {
				vnclog.Print(LL_INTERR, VNCLOG("unable to set DIB section palette"), GetLastError());
//				return FALSE;
			}
			if (!::SelectObject(bitmapDC, old_bitmap)) {
				vnclog.Print(LL_INTERR, VNCLOG("unable to restore temporary DC bitmap"), GetLastError());
                DeleteObject(old_bitmap);
                DeleteDC(bitmapDC);
				return FALSE;
			}
            DeleteObject(old_bitmap);
            DeleteDC(bitmapDC);
            return TRUE;
		}
    }
 
	// Not a palette based local screen - forget it!
	vnclog.Print(LL_INTERR, VNCLOG("no palette data for truecolour display\n"));
	return TRUE;
}
////////////////////////////////////////////////////////////////////////////////
DWORD WINAPI Driverwatch(LPVOID lpParam)
{
	//Mouse shape changed
	if (OSversion()==1 || OSversion()==2)
	{
		HANDLE event;
		//DrvWatch *mywatch=(DrvWatch*)lpParam;
		HWND hwnd=(HWND)lpParam;
		event=NULL;
		while (event==NULL)
		{
			event = OpenEvent (SYNCHRONIZE, FALSE, "VncEvent") ;
			Sleep(900);
			if (!IsWindow(hwnd)) 
			{
				if (event) CloseHandle(event);
				return 0;
			}
		}
		SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL);
		for (;;)
		{
		if (WaitForSingleObject(event, 2000) == WAIT_OBJECT_0)
			{
				PostMessage(hwnd, WM_MOUSESHAPE, 0, 0);
			}
		if (!IsWindow(hwnd) || !g_Desktop_running)
		{
			if (event) CloseHandle(event);
			break;
		}
		}
	}
	return 0;
}



DWORD
vncDesktop::EnableOptimisedBlits()
{
	vnclog.Print(LL_INTINFO, VNCLOG("attempting to enable DIBsection blits\n"));

	// Create a new DIB section
	//HBITMAP tempbitmap=NULL;
	HBITMAP tempbitmap = CreateDIBSection(m_hmemdc, &m_bminfo.bmi, DIB_RGB_COLORS, &m_DIBbits, NULL, 0);
	if (tempbitmap == NULL) {
		vnclog.Print(LL_INTINFO, VNCLOG("failed to build DIB section - reverting to slow blits\n"));
		m_DIBbits = NULL;
        tempbitmap = CreateCompatibleBitmap(m_hrootdc, m_bmrect.br.x, m_bmrect.br.y);
	    if (tempbitmap == NULL) {
		    vnclog.Print(LL_INTERR, VNCLOG("failed to create memory bitmap(%d)\n"), GetLastError());
		    return ERROR_DESKTOP_NO_DIBSECTION_OR_COMPATBITMAP;
	    }
	    else
	        vnclog.Print(LL_INTINFO, VNCLOG("enabled slow blits OK\n"));
	}
    else
        vnclog.Print(LL_INTINFO, VNCLOG("enabled fast DIBsection blits OK\n"));

	// Delete the old memory bitmap
	if (m_membitmap != NULL) {
		DeleteObject(m_membitmap);
		m_membitmap = NULL;
	}

	// Replace old membitmap with DIB section
	m_membitmap = tempbitmap;
    return 0;
}

DWORD
vncDesktop::Init(vncServer *server)
{
	vnclog.Print(LL_INTINFO, VNCLOG("initialising desktop handler\n"));

	// Save the server pointer
	m_server = server;

	// sf@2005
	if (OSversion()==1 || OSversion()==2)
	m_fCaptureAlphaBlending = m_server->CaptureAlphaBlending();
	else m_fCaptureAlphaBlending=false;

	// Load in the arrow cursor
	m_hdefcursor = LoadCursor(NULL, IDC_ARROW);
	m_hcursor = m_hdefcursor;
	m_hOldcursor = m_hdefcursor; //sf@2002

	// Spawn a thread to handle that window's message queue
	vncDesktopThread *thread = new vncDesktopThread;
	if (thread == NULL) {
		vnclog.Print(LL_INTERR, VNCLOG("failed to start hook thread\n"));
		return ERROR_DESKTOP_NO_DESKTOPTHREAD;
	}
	m_thread = thread;
	//SINGEL WINDOW
	SWinit();
	// InitHookSettings();
	return thread->Init(this, m_server);
}

int
vncDesktop::ScreenBuffSize()
{
	return m_scrinfo.format.bitsPerPixel/8 *
		m_scrinfo.framebufferWidth *
		m_scrinfo.framebufferHeight;
}

void
vncDesktop::FillDisplayInfo(rfbServerInitMsg *scrinfo)
{
	memcpy(scrinfo, &m_scrinfo, sz_rfbServerInitMsg);
}


void
vncDesktop::WriteMessageOnScreen(char * tt,BYTE *scrBuff, UINT scrBuffSize)
{
	// Select the memory bitmap into the memory DC
	RECT rect;
	SetRect(&rect, 0,0, 300, 120);
	COLORREF bgcol =RGB(0xff, 0x00, 0x00);
	COLORREF oldtxtcol =SetTextColor(m_hmemdc, RGB(0,0,254));
	COLORREF oldbgcol  =SetBkColor(  m_hmemdc, bgcol);
	HBRUSH backgroundBrush = CreateSolidBrush(bgcol);

	HBITMAP oldbitmap;
	if ((oldbitmap = (HBITMAP) SelectObject(m_hmemdc, m_membitmap)) == NULL)
					return;

	FillRect(m_hmemdc, &rect, backgroundBrush); 
	DrawText(m_hmemdc,tt,strlen(tt),&rect,DT_CENTER | DT_VCENTER);
				// Select the old bitmap back into the memory DC
	//DWORD err= GetLastError();

	
	//ExtTextOut(m_hmemdc, 0, 0, ETO_OPAQUE,&rect,tt,strlen(tt), NULL); 
        

	SetBkColor(  m_hmemdc, oldbgcol);
	SetTextColor(m_hmemdc, oldtxtcol);
	SelectObject(m_hmemdc, oldbitmap);
	DeleteObject(backgroundBrush); 

	CopyToBuffer(rect, scrBuff, scrBuffSize);
}

#ifndef CAPTUREBLT
#define CAPTUREBLT  0x40000000
#endif

// Function to capture an area of the screen immediately prior to sending
// an update.
void
vncDesktop::CaptureScreen(const rfb::Rect &rect, BYTE *scrBuff, UINT scrBuffSize,BOOL capture)
{
	assert(rect.enclosed_by(m_bmrect));
	if (capture)
	{
		if (OSversion()==1 || OSversion()==2)
		m_fCaptureAlphaBlending = m_server->CaptureAlphaBlending();


		// Select the memory bitmap into the memory DC
		HBITMAP oldbitmap;
		if ((oldbitmap = (HBITMAP) SelectObject(m_hmemdc, m_membitmap)) == NULL)
			return;

	#if defined(_DEBUG)
		DWORD t = timeGetTime();
	#endif
		// Capture screen into bitmap
		BOOL blitok = false;
		if (multi_monitor)
		{
		blitok=BitBlt(m_hmemdc, 
			 rect.tl.x,
			 rect.tl.y,
			(rect.br.x-rect.tl.x),
			(rect.br.y-rect.tl.y),
			m_hrootdc,
			rect.tl.x+m_ScreenOffsetx,
			rect.tl.y+m_ScreenOffsety,
			(m_fCaptureAlphaBlending && !m_Black_window_active) ? (CAPTUREBLT | SRCCOPY) : SRCCOPY
			);
		}
		else
		{
			blitok=BitBlt(m_hmemdc, 
			 rect.tl.x,
			 rect.tl.y,
			(rect.br.x-rect.tl.x),
			(rect.br.y-rect.tl.y),
			m_hrootdc, rect.tl.x, rect.tl.y, (m_fCaptureAlphaBlending && !m_Black_window_active) ? (CAPTUREBLT | SRCCOPY) : SRCCOPY);
		}
	#if defined(_DEBUG)
		DWORD e = timeGetTime() - t;
		vnclog.Print(LL_INTWARN, VNCLOG("Blit (%u,%u - %u,%u) (%ux%u took %ums\n"), 
			rect.tl.x, rect.tl.y, rect.br.x, rect.br.y,
			rect.width(), rect.height(), e);
	#endif
	#if 0
		//*******************************
		static int cycle(0);
		static COLORREF c[] = {RGB(0xFF,0,0), RGB(0,0xFF,0),RGB(0,0,0xFF), RGB(0xFF, 0,0xFF), RGB(0,0xFF,0xFF)};
	HPEN pen = CreatePen(PS_SOLID|PS_INSIDEFRAME, 2, c[cycle]);
	HPEN oldpen = (HPEN)SelectObject(m_hmemdc, pen);
	SelectObject(m_hmemdc, GetStockObject(HOLLOW_BRUSH));
	Rectangle(m_hmemdc, rect.tl.x, rect.tl.y, rect.br.x, rect.br.y);
	SelectObject(m_hmemdc, oldpen);
	DeleteObject(pen);
		cycle = (cycle + 1) % (sizeof c / sizeof c[0]);
		//*******************************
	#endif
		// Select the old bitmap back into the memory DC
		SelectObject(m_hmemdc, oldbitmap);

		if (blitok)
		{
			// Copy the new data to the screen buffer (CopyToBuffer optimises this if possible)
			CopyToBuffer(rect, scrBuff, scrBuffSize);
		}
	}
	else
	{
		CopyToBuffer(rect, scrBuff, scrBuffSize);
	}

}

// Add the mouse pointer to the buffer
void
vncDesktop::CaptureMouse(BYTE *scrBuff, UINT scrBuffSize)
{
	POINT CursorPos;
	ICONINFO IconInfo = {0};

	// If the mouse cursor handle is invalid then forget it
	if (m_hcursor == NULL)
		return;

	// Get the cursor position
	if (!GetCursorPos(&CursorPos))
		return;
	CursorPos.x -= m_ScreenOffsetx;
	CursorPos.y -= m_ScreenOffsety;
	//vnclog.Print(LL_INTINFO, VNCLOG("CursorPos %i %i\n"),CursorPos.x, CursorPos.y);
	// Translate position for hotspot
	if (GetIconInfo(m_hcursor, &IconInfo))
	{
		CursorPos.x -= ((int) IconInfo.xHotspot);
		CursorPos.y -= ((int) IconInfo.yHotspot);
		/// Buffer has (0,0) coordinates, Cursor (screencoordinates)
		CursorPos.x -= m_ScreenOffsetx;
		CursorPos.y -= m_ScreenOffsety;
		///
		
		if (CursorPos.x<=m_bmrect.tl.x || CursorPos.y<=m_bmrect.tl.y || CursorPos.x>=m_bmrect.br.x || CursorPos.y>=m_bmrect.br.y) 
		{
			if ((m_cursorpos.tl.x<=m_bmrect.tl.x)||
				(m_cursorpos.tl.y<=m_bmrect.tl.y)||
				(m_cursorpos.br.x>=m_bmrect.br.x)||
				(m_cursorpos.br.y>=m_bmrect.br.y))
			{
				m_cursorpos.tl.x=0;
				m_cursorpos.tl.y=0;
				m_cursorpos.br.x=16;
				m_cursorpos.br.y=16;
			}
			//Error, cursor isn't on the visbale display.
			if (IconInfo.hbmMask != NULL)
					DeleteObject(IconInfo.hbmMask);
				if (IconInfo.hbmColor != NULL)
					DeleteObject(IconInfo.hbmColor);
			return;
		}

		// Save the bounding rectangle
		m_cursorpos.tl = CursorPos;
		m_cursorpos.br = rfb::Point(GetSystemMetrics(SM_CXCURSOR),
		GetSystemMetrics(SM_CYCURSOR)).translate(CursorPos);
		// Clip the bounding rect to the screen
		// Copy the mouse cursor into the screen buffer, if any of it is visible
		m_cursorpos = m_cursorpos.intersect(m_bmrect);

		if (IconInfo.hbmMask && IconInfo.hbmColor)
			{
					HBITMAP oldbitmap;
					if ((oldbitmap = (HBITMAP) SelectObject(m_hmemdc, m_membitmap)) == NULL)
							return;
					HDC mdc1, mdc2;
					mdc1 = CreateCompatibleDC(m_hmemdc);
					mdc2 = CreateCompatibleDC(m_hmemdc);
					HBITMAP oldbmp1 = (HBITMAP)SelectObject(mdc1, IconInfo.hbmMask);
					HBITMAP oldbmp2 = (HBITMAP)SelectObject(mdc2, IconInfo.hbmColor);
					BitBlt(m_hmemdc,m_cursorpos.tl.x, m_cursorpos.tl.y,m_cursorpos.br.x-m_cursorpos.tl.x, m_cursorpos.br.y-m_cursorpos.tl.y, mdc1, 0, 0, SRCAND);
					BitBlt(m_hmemdc, m_cursorpos.tl.x, m_cursorpos.tl.y, m_cursorpos.br.x-m_cursorpos.tl.x, m_cursorpos.br.y-m_cursorpos.tl.y, mdc2, 0, 0, SRCINVERT);

					SelectObject(m_hmemdc, oldbitmap);
					SelectObject(mdc1, oldbmp1);
					SelectObject(mdc2, oldbmp2);
					DeleteDC(mdc1);
					DeleteDC(mdc2);
			}
		else
			{

				// Select the memory bitmap into the memory DC
				HBITMAP oldbitmap;
				if ((oldbitmap = (HBITMAP) SelectObject(m_hmemdc, m_membitmap)) == NULL)
					return;

				// Draw the cursor
				DrawIconEx(
					m_hmemdc,									// handle to device context 
					CursorPos.x, CursorPos.y,
					m_hcursor,									// handle to icon to draw 
					0,0,										// width of the icon 
					0,											// index of frame in animated cursor 
					NULL,										// handle to background brush 
					DI_NORMAL | DI_COMPAT						// icon-drawing flags 
					);

				// Select the old bitmap back into the memory DC
				SelectObject(m_hmemdc, oldbitmap);
		}

		if (IconInfo.hbmMask != NULL)
			DeleteObject(IconInfo.hbmMask);
		if (IconInfo.hbmColor != NULL)
			DeleteObject(IconInfo.hbmColor);
	}


	if (!m_cursorpos.is_empty()) {
		CopyToBuffer(m_cursorpos, scrBuff, scrBuffSize);
	}
}


// CURSOR HANDLING
// Obtain cursor image data in server's local format.
// The length of databuf[] should be at least (width * height * 4).
BOOL
vncDesktop::GetRichCursorData(BYTE *databuf, HCURSOR hcursor, int width, int height)
{
	// Protect the memory bitmap (is it really necessary here?)
	omni_mutex_lock l(m_update_lock);

	// Create bitmap, select it into memory DC
	HBITMAP membitmap = CreateCompatibleBitmap(m_hrootdc, width, height);
	if (membitmap == NULL) {
		return FALSE;
	}
	HBITMAP oldbitmap = (HBITMAP) SelectObject(m_hmemdc, membitmap);
	if (oldbitmap == NULL) {
		DeleteObject(membitmap);
		return FALSE;
	}

	// Draw the cursor
	DrawIconEx(m_hmemdc, 0, 0, hcursor, 0, 0, 0, NULL, DI_IMAGE);
	SelectObject(m_hmemdc, oldbitmap);

	// Prepare BITMAPINFO structure (copy most m_bminfo fields)
	BITMAPINFO *bmi = (BITMAPINFO *)calloc(1, sizeof(BITMAPINFO) + 256 * sizeof(RGBQUAD));
	int lines=0;
	if (bmi)
	{
	memcpy(bmi, &m_bminfo.bmi, sizeof(BITMAPINFO) + 256 * sizeof(RGBQUAD));
	bmi->bmiHeader.biWidth = width;
	bmi->bmiHeader.biHeight = -height;

	// Clear data buffer and extract RGB data
	memset(databuf, 0x00, width * height * 4);
	lines = GetDIBits(m_hmemdc, membitmap, 0, height, databuf, bmi, DIB_RGB_COLORS);

	// Cleanup
	free(bmi);
	}
	DeleteObject(membitmap);

	return (lines != 0);
}

// Return the current mouse pointer position
rfb::Rect
vncDesktop::MouseRect()
{
	return m_cursorpos;
}

void
vncDesktop::SetCursor(HCURSOR cursor)
{
	if (cursor == NULL)
		m_hcursor = m_hdefcursor;
	else
	{
		m_hOldcursor = m_hcursor; // sf@2002
		m_hcursor = cursor;
	}
}

// Manipulation of the clipboard
void vncDesktop::SetClipText(char* rfbStr)
{
  // Open the system clipboard
  if (OpenClipboard(m_hwnd))
  {
    // Empty it
    if (EmptyClipboard())
    {
	  // adzm - 2010-07 - Extended clipboard
      HANDLE hMem = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, strlen(rfbStr) + 1);

      if (hMem != NULL)
      {
        LPSTR pMem = (char*)GlobalLock(hMem);

        // Get the data
		if (pMem)
		{
			strcpy(pMem, rfbStr);
        	// Tell the clipboard
        	GlobalUnlock(hMem);
        	SetClipboardData(CF_TEXT, hMem);
		}
      }
    }
  }

  // Now close it
  CloseClipboard();
}

// adzm - 2010-07 - Extended clipboard
void vncDesktop::SetClipTextEx(ExtendedClipboardDataMessage& extendedClipboardDataMessage)
{
	bool bRestored = false;
	{
		ClipboardData newClipboard;

		if (newClipboard.Restore(Window(), extendedClipboardDataMessage)) {
			vnclog.Print(LL_INTINFO, VNCLOG("Set extended clipboard data\n"));

			bRestored = true;
			newClipboard.FreeData();
		} else {
			vnclog.Print(LL_INTWARN, VNCLOG("Failed to set extended clipboard data\n"));
		}
	}
}

// INTERNAL METHODS

inline void
vncDesktop::MaskToMaxAndShift(DWORD mask, CARD16 &max, CARD8 &shift)
{
	for (shift = 0; (mask & 1) == 0; shift++)
		mask >>= 1;
	max = (CARD16) mask;
}

// Copy data from the memory bitmap into a buffer
void
vncDesktop::CopyToBuffer(const rfb::Rect &rect, BYTE *destbuff, UINT destbuffsize)
{
	// Finish drawing anything in this thread 
	// Wish we could do this for the whole system - maybe we should
	// do something with LockWindowUpdate here.
	GdiFlush();

	// Are we being asked to blit from the DIBsection to itself?
	if (destbuff == m_DIBbits) {
		// Yes.  Ignore the request!
		return;
	}

	int y_inv;
	BYTE * destbuffpos;

	// Calculate the scanline-ordered y position to copy from
	y_inv = m_scrinfo.framebufferHeight-rect.tl.y-(rect.br.y-rect.tl.y);

	// Calculate where in the output buffer to put the data
	destbuffpos = destbuff + (m_bytesPerRow * rect.tl.y);

	// Set the number of bytes for GetDIBits to actually write
	// NOTE : GetDIBits pads the destination buffer if biSizeImage < no. of bytes required
	m_bminfo.bmi.bmiHeader.biSizeImage = (rect.br.y-rect.tl.y) * m_bytesPerRow;

	// Get the actual bits from the bitmap into the bit buffer
	// If fast (DIBsection) blits are disabled then use the old GetDIBits technique
	if (m_DIBbits == NULL) {
		if (GetDIBits(m_hmemdc, m_membitmap, y_inv,
					(rect.br.y-rect.tl.y), destbuffpos,
					&m_bminfo.bmi, DIB_RGB_COLORS) == 0)
		{
#ifdef _MSC_VER
			_RPT1(_CRT_WARN, "vncDesktop : [1] GetDIBits failed! %d\n", GetLastError());
			_RPT3(_CRT_WARN, "vncDesktop : thread = %d, DC = %d, bitmap = %d\n", omni_thread::self(), m_hmemdc, m_membitmap);
			_RPT2(_CRT_WARN, "vncDesktop : y = %d, height = %d\n", y_inv, (rect.br.y-rect.tl.y));
#endif
		}
	} else {
		// Fast blits are enabled.  [I have a sneaking suspicion this will never get used, unless
		// something weird goes wrong in the code.  It's here to keep the function general, though!]

		int bytesPerPixel = m_scrinfo.format.bitsPerPixel / 8;
		BYTE *srcbuffpos = (BYTE*)m_DIBbits;

		srcbuffpos += (m_bytesPerRow * rect.tl.y) + (bytesPerPixel * rect.tl.x);
		destbuffpos += bytesPerPixel * rect.tl.x;

		int widthBytes = (rect.br.x-rect.tl.x) * bytesPerPixel;

		for(int y = rect.tl.y; y < rect.br.y; y++)
		{
			memcpy(destbuffpos, srcbuffpos, widthBytes);
			srcbuffpos += m_bytesPerRow;
			destbuffpos += m_bytesPerRow;
		}
	}
}

// Routine to find out which windows have moved
// If copyrect detection isn't perfect then this call returns
// the copyrect destination region, to allow the caller to check
// for mistakes
bool
vncDesktop::CalcCopyRects(rfb::UpdateTracker &tracker)
{
	HWND foreground = GetForegroundWindow();
	RECT foreground_rect;

	// Actually, we just compare the new and old foreground window & its position
	if (foreground != m_foreground_window) {
		m_foreground_window=foreground;
		// Is the window invisible or can we not get its rect?
		if (!IsWindowVisible(foreground) ||
			!GetWindowRect(foreground, &foreground_rect)) {
			//Buffer coordinates
			m_foreground_window_rect.clear();
		} else {
			foreground_rect.left-=m_ScreenOffsetx;
			foreground_rect.right-=m_ScreenOffsetx;
			foreground_rect.top-=m_ScreenOffsety;
			foreground_rect.bottom-=m_ScreenOffsety;
			m_foreground_window_rect = foreground_rect;
		}
	} else {
		// Same window is in the foreground - let's see if it's moved
		RECT destrect;
		rfb::Rect dest;
		rfb::Point source;

		// Get the window rectangle
		if (IsWindowVisible(foreground) && GetWindowRect(foreground, &destrect))
		{
			//vnclog.Print(LL_INTERR, VNCLOG("foreground %i %i %i %i\n"),destrect.left,destrect.right,destrect.top,destrect.bottom);
			//screen to buffer coordinates
			destrect.left-=m_ScreenOffsetx;
			destrect.right-=m_ScreenOffsetx;
			destrect.top-=m_ScreenOffsety;
			destrect.bottom-=m_ScreenOffsety;
			//vnclog.Print(LL_INTERR, VNCLOG("foreground %i %i %i %i\n"),destrect.left,destrect.right,destrect.top,destrect.bottom);

			//We cant have negative coordinates
			//move window on desktop 1 and show desktop 2
			if (destrect.left<0 || destrect.right<0 || destrect.top<0 || destrect.bottom<0) return false;
			// we also can have to big coordinates
			// move window on desktop2 and show desktop 1
			if (destrect.left > m_Cliprect.br.x ||destrect.right > m_Cliprect.br.x ||destrect.top > m_Cliprect.br.y ||destrect.bottom > m_Cliprect.br.y)
			return false;


			rfb::Rect old_foreground_window_rect = m_foreground_window_rect;
			source = m_foreground_window_rect.tl;
			m_foreground_window_rect = dest = destrect;
			//vnclog.Print(LL_INTERR, VNCLOG("dest %i %i %i %i\n"),dest.tl.x,dest.br.x,dest.tl.y,dest.br.y);
			//vnclog.Print(LL_INTERR, VNCLOG("source %i %i \n"),source.x,source.y);

			if (!dest.is_empty() && !old_foreground_window_rect.is_empty())
			{
				// Got the destination position.  Now send to clients!
				if (!source.equals(dest.tl))
				{
					rfb::Point delta;
					delta= rfb::Point(dest.tl.x-source.x, dest.tl.y-source.y);
				//	if (dest.tl.x-source.x==0 || dest.tl.y-source.y==0) return false;
					//vnclog.Print(LL_INTERR, VNCLOG("delta %i %i \n"),delta.x,delta.y);

					// Clip the destination rectangle
					dest = dest.intersect(m_bmrect);
					//vnclog.Print(LL_INTERR, VNCLOG("dest2 %i %i %i %i\n"),dest.tl.x,dest.br.x,dest.tl.y,dest.br.y);
					if (dest.is_empty()) return false;
					// Clip the source rectangle
					dest = dest.translate(delta.negate()).intersect(m_bmrect);
					//vnclog.Print(LL_INTERR, VNCLOG("dest3 %i %i %i %i\n"),dest.tl.x,dest.br.x,dest.tl.y,dest.br.y);
					if (dest.tl.x<0 || dest.br.x<0 || dest.tl.y<0 || dest.br.y<0) return false;
					if (dest.tl.x > m_Cliprect.br.x ||dest.br.x > m_Cliprect.br.x ||dest.tl.y > m_Cliprect.br.y ||dest.br.y > m_Cliprect.br.y)
					return false;
					m_buffer.ClearCacheRect(dest);
					dest = dest.translate(delta);
					//vnclog.Print(LL_INTERR, VNCLOG("dest4 %i %i %i %i\n"),dest.tl.x,dest.br.x,dest.tl.y,dest.br.y);
					m_buffer.ClearCacheRect(dest);
					if (!dest.is_empty()) {
						tracker.add_copied(dest, delta);
						return true;
					}
				}
			}
		} else {
			m_foreground_window_rect.clear();
		}
	}
	return false;
}


// added jef
void vncDesktop::SetBlankMonitor(bool enabled)
{

	// Added Jef Fix
	typedef DWORD (WINAPI *PSLWA)(HWND, DWORD, BYTE, DWORD);
	PSLWA pSetLayeredWindowAttributes=NULL;
	HMODULE hDLL = LoadLibrary ("user32");
	if (hDLL) pSetLayeredWindowAttributes = (PSLWA) GetProcAddress(hDLL,"SetLayeredWindowAttributes");
	if (!pSetLayeredWindowAttributes) m_server->BlackAlphaBlending(false);
	//if (VideoBuffer()) m_server->BlackAlphaBlending(false);

    vnclog.Print(LL_INTINFO, VNCLOG("SetBlankMonitor: monitor %s, using alpha %s\n"), 
        enabled ? "off" : "on",
        m_server->BlackAlphaBlending() ? "true" : "false");

	// Also Turn Off the Monitor if allowed ("Blank Screen", "Blank Monitor")
	if (m_server->BlankMonitorEnabled())
    {
	    if (enabled)
	    {
		    if ((!m_server->BlackAlphaBlending() || VideoBuffer()) && (OSversion()!=2))
		    {
			    SetProcessShutdownParameters(0x100, 0);
			    SystemParametersInfo(SPI_GETPOWEROFFTIMEOUT, 0, &OldPowerOffTimeout, 0);
			    SystemParametersInfo(SPI_SETPOWEROFFTIMEOUT, 100, NULL, 0);
			    SystemParametersInfo(SPI_SETPOWEROFFACTIVE, 1, NULL, 0);
			    SendMessage(m_hwnd,WM_SYSCOMMAND,SC_MONITORPOWER,(LPARAM)2);
		    }
		    else
		    {
			    HANDLE ThreadHandle2=NULL;
			    DWORD dwTId;
			    ThreadHandle2 = CreateThread(NULL, 0, BlackWindow, NULL, 0, &dwTId);
			   if (ThreadHandle2)  CloseHandle(ThreadHandle2);
			    m_Black_window_active=true;
		    }
	    }
	    else // Monitor On
	    {
		    if ((!m_server->BlackAlphaBlending() || VideoBuffer()) && (OSversion()!=2))
		    {
			    if (OldPowerOffTimeout!=0)
				    SystemParametersInfo(SPI_SETPOWEROFFTIMEOUT, OldPowerOffTimeout, NULL, 0);
			    SystemParametersInfo(SPI_SETPOWEROFFACTIVE, 0, NULL, 0);
			    if (m_hwnd!=NULL) SendMessage(m_hwnd,WM_SYSCOMMAND,SC_MONITORPOWER,(LPARAM)-1);
			    OldPowerOffTimeout=0;
				//JUst in case video driver state was changed
				HWND Blackhnd = FindWindow(("blackscreen"), 0);
			    if (Blackhnd) PostMessage(Blackhnd, WM_CLOSE, 0, 0);
		    }
		    else
		    {
			    HWND Blackhnd = FindWindow(("blackscreen"), 0);
			    if (Blackhnd) PostMessage(Blackhnd, WM_CLOSE, 0, 0);
			    m_Black_window_active=false;
		    }
	    }
    }
}


// Modif rdv@2002 Dis/enable input
void
vncDesktop::SetDisableInput()
{
    CARD32 state;
    state=!block_input();
    m_server->NotifyClients_StateChange(rfbServerRemoteInputsState, state);
}


// SW
void vncDesktop::SetSW(int x,int y)
{
	POINT point;
	point.x=x;
	point.y=y;
	MONITORINFO monitorinfo;
	monitorinfo.cbSize=sizeof(MONITORINFO);

    helper::DynamicFn<LPGETMONITORINFO> GetMonitorInfo("USER32","GetMonitorInfoA");
    helper::DynamicFn<LPMONITOTFROMPOINT> MonitorFromPoint("USER32","MonitorFromPointA");

	if (x <= 5 && y<=5 && x>-5 && y>-5) 
		{
			// 2= multi monitor
			// 1= single monitor
			/* yz
			if (requested_multi_monitor)m_buffer.MultiMonitors(1);
			else m_buffer.MultiMonitors(2);
			*/
			switch (nr_monitors) {
			case 2:
				{
					if (m_current_monitor == MULTI_MON_PRIMARY) {
						m_current_monitor = MULTI_MON_SECOND;
						m_buffer.MultiMonitors(1);
					} else if (m_current_monitor == MULTI_MON_SECOND) {
						m_current_monitor = MULTI_MON_ALL;
						m_buffer.MultiMonitors(2);
					} else if (m_current_monitor == MULTI_MON_ALL) {
						m_buffer.MultiMonitors(1);
						m_current_monitor = MULTI_MON_PRIMARY;
					} 
				} break;
			case 3:
				{
					if (m_current_monitor == MULTI_MON_PRIMARY) {
						m_current_monitor = MULTI_MON_SECOND;
						m_buffer.MultiMonitors(1);
					} else if (m_current_monitor == MULTI_MON_SECOND) {
						m_current_monitor = MULTI_MON_THIRD;
						m_buffer.MultiMonitors(1);
					} else if (m_current_monitor == MULTI_MON_THIRD) {
						m_current_monitor = MULTI_MON_FIRST_TWO;
						m_buffer.MultiMonitors(2);
					} else if (m_current_monitor == MULTI_MON_FIRST_TWO) {
						m_current_monitor = MULTI_MON_LAST_TWO;
						m_buffer.MultiMonitors(2);
					} else if (m_current_monitor == MULTI_MON_LAST_TWO) {
						m_current_monitor = MULTI_MON_ALL;
						m_buffer.MultiMonitors(2);
					} else if (m_current_monitor == MULTI_MON_ALL) {
						m_current_monitor = MULTI_MON_PRIMARY;
						m_buffer.MultiMonitors(1);
					} 
				} break;
			}

			m_Single_hWnd=NULL;
			return;
		}
	//SW is disabled seems to crash server
	//until found and fixed we better disabled it to avoid
	//this
	return;

	m_Single_hWnd=WindowFromPoint(point);

	if (m_Single_hWnd==GetDesktopWindow())
		{
			if (GetMonitorInfo.isValid() && MonitorFromPoint.isValid())
			{
				HMONITOR hmonitor= (*MonitorFromPoint)(point,MONITOR_DEFAULTTONEAREST);
				(*GetMonitorInfo)(hmonitor,&monitorinfo);
				if (monitorinfo.dwFlags ==MONITORINFOF_PRIMARY) 
				{
					m_buffer.MultiMonitors(1);
					m_Single_hWnd=NULL;
				}
				else
				{
					m_buffer.MultiMonitors(2);
					m_Single_hWnd=NULL;
				}
			}
			return;
		}

	if (m_Single_hWnd==NULL) m_server->SingleWindow(false);
	else
		{
		HWND parent;
		while((parent=GetParent(m_Single_hWnd))!=NULL)
			{
				m_Single_hWnd=parent;
			}
		m_server->SingleWindow(true);
		}

}

/***************************************************************************
* Ultravnc use 2 different software hooking methods 
* hookdll: Hook the messages between the windows (nt,9.x)
* ddihook: Hook the messages to the video buffer (9.x)
* Driver hooking
* Driver hooking is done at kernel level.
* and exist of 2 parts
* update mechanism: rectangles are send from kernel to vnc (shared mem)
* shared video buffer: direct access the memeory without bitblit
*  m_hookdriver: use driver update mechanism (ddihook in case of 9.x)
*  m_hookdll: use software update mechanism
***************************************************************************/

// Modif rdv@2002 - v1.1.x - videodriver

BOOL vncDesktop::VideoBuffer()
{
	//Always access the shared mememory thru this function
	// Check NULL
	if (m_videodriver==NULL) return FALSE;
	if (m_videodriver->mypVideoMemory) return true;

	if (!m_videodriver->mypVideoMemory)
	{
		m_hookswitch=true;
		Hookdll_Changed=true;
		return FALSE;
	}
	return FALSE;
}

DWORD WINAPI Warningbox_non_locked(LPVOID lpParam)
{
	MessageBoxSecure(NULL,"Current driver to old for this version \nUpdate driver or disable Video hook driver\n in the server properties window","",0);
	return 0;
}

// Modif rdv@2002 - v1.1.x - videodriver
BOOL vncDesktop::InitVideoDriver()
{
	omni_mutex_lock l(m_videodriver_lock);
	
	if(!(OSversion()==1  || OSversion()==2)) return true; //we need w2k or xp
	vnclog.Print(LL_INTERR, VNCLOG("Driver option is enabled\n"));
	// If m_videodriver exist, the driver was activated.
	// This does not mean he is still active
	// Screen switching disable the driver at kernel level
	// The pointers to the shared memory still exist, but no more memeory
	// associated...This is the biggest risk when using the driver
	//

	// First check driver version
	if (m_videodriver!=NULL) 
	{
		
								vnclog.Print(LL_INTERR, VNCLOG("Closing pending driver driver version\n"));
								if (m_videodriver!=NULL) delete m_videodriver;
			
	}

	m_videodriver=new VIDEODRIVER;
	//try to use the mirror driver if he is still active
	Checkmonitors();
	nr_monitors=GetNrMonitors();
	if (nr_monitors==1)
		{
			m_ScreenOffsetx=mymonitor[0].offsetx;
			m_ScreenOffsety=mymonitor[0].offsety;
			m_videodriver->VIDEODRIVER_start(mymonitor[0].offsetx,mymonitor[0].offsety,mymonitor[0].Width,mymonitor[0].Height);
		}
	if (nr_monitors>1)
		{
			/* JnZn558
			m_ScreenOffsetx=mymonitor[2].offsetx;
			m_ScreenOffsety=mymonitor[2].offsety;
			m_videodriver->VIDEODRIVER_start(mymonitor[2].offsetx,mymonitor[2].offsety,mymonitor[2].Width,mymonitor[2].Height);
			*/
			m_ScreenOffsetx=mymonitor[3].offsetx;
			m_ScreenOffsety=mymonitor[3].offsety;
			m_videodriver->VIDEODRIVER_start(mymonitor[3].offsetx,mymonitor[3].offsety,mymonitor[3].Width,mymonitor[3].Height);
		}		
	vnclog.Print(LL_INTERR, VNCLOG("Start Mirror driver\n"));
	m_hookdriver=true;
	m_hookdll=false;
	// check if driver has mapped the shared memory
	if (!m_videodriver->mypVideoMemory) 
	{
		vnclog.Print(LL_INTERR, VNCLOG("Start Mirror driver Failed\n"));
		vnclog.Print(LL_INTERR, VNCLOG("Using non driver mode\n"));
		if (m_videodriver!=NULL) delete m_videodriver;
		m_videodriver=NULL;
		// If driver selected and fialed to start default to hookdll
		// 
		m_hookdriver=false;
		m_hookdll=true;
		// sf@2002 - Necessary for the following InitHookSettings() call
		// Remember old states
		DriverWantedSet=true;
		DriverWanted=m_server->Driver();
		HookWanted=m_server->Hook();
		m_server->Driver(false);
		m_server->Hook(true);
		return false;
	}
	
	if (m_videodriver->mypVideoMemory)
	{
		vnclog.Print(LL_INTERR, VNCLOG("Driver Used\n"));
		if (!m_videodriver->mypVideoMemory)
		{
			vnclog.Print(LL_INTERR, VNCLOG("Unable to map memory\n"));
			delete m_videodriver;
			m_videodriver=NULL;
			// If driver selected and fialed to start default to hookdll
			// 
			m_hookdriver=false;
			m_hookdll=true;
			return false;
		}
		vnclog.Print(LL_INTERR, VNCLOG("Shared memory mapped\n"));
		InvalidateRect(NULL,NULL,TRUE);

		return true;
	}
	return true;
}


// Modif rdv@2002 - v1.1.x - videodriver
void vncDesktop::ShutdownVideoDriver()
{
	if(!(OSversion()!=1 || OSversion()!=2)) return;
	if (m_videodriver==NULL) return;
	if (m_videodriver!=NULL)
	{
		delete m_videodriver;
		m_videodriver=NULL;
	}
	StopDriverWatches=true;
}


//
// This proc sets the Desktop update handling params depending
// on tranitted settings and internal constraints.
// It is called from several places (InitHookSettings(), SetHookings()
// and from Desktop thread loop).
// 
void vncDesktop::SethookMechanism(BOOL hookall,BOOL hookdriver)
{
	m_hookswitch=false;
	// Force at least one updates handling method !
	if (!hookall && !hookdriver && !m_server->PollFullScreen())
	{
		hookall = TRUE;
		m_server->PollFullScreen(TRUE);
	}

	// 9,x case 
	vnclog.Print(LL_INTERR, VNCLOG("SethookMechanism called\r\n"));
	if(OSversion()==4 || OSversion()==5)
	{
		m_hookdriver=false;//(user driver updates)
		m_hookdll=false;//(use hookdll updates)
		if (hookall || hookdriver) m_hookdll=true;
		//always try to stop the ddihook before starting
		StartStopddihook(false);ddihook=false;
		if (hookdriver) StartStopddihook(true);
		// same for the hookdll
		BOOL old_On_Off_hookdll=On_Off_hookdll;
		if (hookdriver || hookall) On_Off_hookdll=true;
		else On_Off_hookdll=false;
		if (old_On_Off_hookdll!=On_Off_hookdll) Hookdll_Changed=true;
		else Hookdll_Changed=false;
	}
	// W2k-XP case
	else if(OSversion()==1 || OSversion()==2)
	{
		// sf@2002 - We forbid hoodll and hookdriver at the same time (pointless and high CPU load)
		if (!hookall && !hookdriver) {m_hookdll=false;m_hookdriver=false;}
		if (hookall && hookdriver) {m_hookdll=false;m_hookdriver=true;}
		if (hookall && !hookdriver) {m_hookdll=true;m_hookdriver=false;}
		if (!hookall && hookdriver) {m_hookdll=false;m_hookdriver=true;}

		BOOL old_On_Off_hookdll=On_Off_hookdll;
		if (m_hookdll) On_Off_hookdll=true;
		else On_Off_hookdll=false;
		if (old_On_Off_hookdll!=On_Off_hookdll) Hookdll_Changed=true;
		else Hookdll_Changed=false;
		if (OSversion()==2) Hookdll_Changed=true;
		
		vnclog.Print(LL_INTERR, VNCLOG("Sethook_restart_wanted hook=%d driver=%d \r\n"),m_hookdll,m_hookdriver);
		if (Hookdll_Changed)
			vnclog.Print(LL_INTERR, VNCLOG("Hookdll status changed \r\n"));

		if ((m_hookdriver && !VideoBuffer()) || (!m_hookdriver && VideoBuffer()))
		{
			m_hookswitch=true;
			vnclog.Print(LL_INTERR, VNCLOG("Driver Status changed\r\n"));
		}
	}
	else //NT4
	{
		if (!hookall && !hookdriver) {m_hookdll=false;m_hookdriver=false;}
		if (hookall && hookdriver) {m_hookdll=true;m_hookdriver=false;}
		if (hookall && !hookdriver) {m_hookdll=true;m_hookdriver=false;}
		if (!hookall && hookdriver) {m_hookdll=false;m_hookdriver=false;}
		BOOL old_On_Off_hookdll=On_Off_hookdll;
		if (m_hookdll) On_Off_hookdll=true;
		else On_Off_hookdll=false;
		if (old_On_Off_hookdll!=On_Off_hookdll) Hookdll_Changed=true;
		else Hookdll_Changed=false;
	}

}
void vncDesktop::StartStopddihook(BOOL enabled)
{
	if (enabled)
	{
		STARTUPINFO ssi;
		PROCESS_INFORMATION ppi;
		m_hddihook=NULL;
		char szCurrentDir[MAX_PATH];
		if (GetModuleFileName(NULL, szCurrentDir, MAX_PATH))
		{
			char* p = strrchr(szCurrentDir, '\\');
			if (p == NULL) return;
			*p = '\0';
			strcat (szCurrentDir,"\\16bithlp.exe");
		}
		// Add ddi hook
		ZeroMemory( &ssi, sizeof(ssi) );
		ssi.cb = sizeof(ssi);
		// Start the child process. 
		if( !CreateProcess( NULL,szCurrentDir, NULL,NULL,FALSE,0,NULL,NULL,&ssi,&ppi ) ) 
		{
			vnclog.Print(LL_INTERR, VNCLOG("set ddihooks Failed\n"));
			ddihook=false;
		}
		else
		{
			vnclog.Print(LL_INTERR, VNCLOG("set ddihooks OK\n"));
			ddihook=true;
			WaitForInputIdle(ppi.hProcess, 10000);
			m_hddihook=ppi.hProcess;
		}
	}
	else
	{
		if (m_hddihook != NULL)
		{
			TerminateProcess(m_hddihook,0);
			m_hddihook=NULL;
			ddihook=false;
		}
	}

}

void vncDesktop::StartStophookdll(BOOL enabled)
{
	vnclog.Print(LL_INTERR, VNCLOG("PostMessage(m_hwnd, WM_HOOKCHANGE \n"));
	PostMessage(m_hwnd, WM_HOOKCHANGE, enabled, 0);
}


//
//
//
void vncDesktop::InitHookSettings()
{
	SethookMechanism(m_server->Hook(),m_server->Driver());
}


void vncDesktop::SetBlockInputState(bool newstate)
{
	CARD32 state;
	if (m_server->BlankMonitorEnabled())
    {
		if (!m_server->BlankInputsOnly()) 
			{
				if (blankmonitorstate==newstate==1) 
					{
						SetBlankMonitor(0);
						blankmonitorstate=0;
					}
				else
				{
					SetBlankMonitor(newstate);	
					blankmonitorstate=newstate;
				}
			}
		m_bIsInputDisabledByClient=newstate;
		state=!block_input();
		
    }
	else state=!newstate;

	m_bIsInputDisabledByClient = !state;

 m_server->NotifyClients_StateChange(rfbServerRemoteInputsState, state);
}

bool vncDesktop::block_input()
{
    int Blockinput_val;
	BOOL returnvalue;
	if(m_bIsInputDisabledByClient || m_server->LocalInputsDisabled())
	{
		Blockinput_val=true;
	}
	else
	{
		Blockinput_val=false;
	}

	/*#ifdef _DEBUG
					char			szText[256];
					sprintf(szText," blockinput %i %i %i %i\n",m_bIsInputDisabledByClient,m_server->LocalInputsDisabled(),old_Blockinput1,old_Blockinput2);
					SetLastError(0);
					OutputDebugString(szText);		
	#endif*/

	if (old_Blockinput1!=m_bIsInputDisabledByClient || old_Blockinput2!=m_server->LocalInputsDisabled())
	{
		CARD32 state;
		state=!Blockinput_val;
		m_server->NotifyClients_StateChange(rfbServerRemoteInputsState, state);
	}
    if (pbi)
    {
        returnvalue = (*pbi)(Blockinput_val);
		DWORD aa=GetLastError();
		if (old_Blockinput!=Blockinput_val && aa==5)
		{
			if (m_hookinited)
			PostMessage(m_hwnd, WM_HOOKCHANGE, 2, 0);
			else Blockinput_val=false;
		}

    }
	old_Blockinput1=m_bIsInputDisabledByClient;
	old_Blockinput2=m_server->LocalInputsDisabled();
	old_Blockinput=Blockinput_val;

    return Blockinput_val;
}
